import { Db, MongoClient, MongoError } from 'mongodb';
import { Config } from '../config';

const mongo = require('mongodb').MongoClient;

export class MongoDb {
	static connect(): Promise<Db> {
		if (!this.connected) {
			return new Promise((resolve, reject) => {
				mongo.connect(this.config.database.url, {
					useNewUrlParser: true,
					useUnifiedTopology: true,
				}, (err: MongoError, client: MongoClient) => {
					if (err) {
						reject(err);
					} else {
						this.client = client;
						resolve(client.db(this.config.database.name));
					}
				});
			});
		} else {
			return new Promise<Db>(resolve => {
				resolve(this.client.db(this.config.database.name));
			});
		}
	}

	static init(config: Config): Promise<void> {
		this.config = config;
		return new Promise((resolve, reject) => {
			mongo.connect(config.database.url + '/' + config.database.name, {
				useNewUrlParser: true,
				useUnifiedTopology: true,
			}, (err: MongoError, client: MongoClient) => {
				if (err) {
					reject();
				} else {
					this.connected = true;
					this.client = client;
					resolve();
				}
			});
		});
	}

	public static close() {
		this.client.close(true);
	}

	public static getCollection<T>(collection: Collections) {
		return this.client.db(this.config.database.name).collection<T>(collection);
	}

	public static findOne<T>(collection: Collections, query: any, ctor: { new(): T }): Promise<T> {
		return new Promise((resolve, reject) => {
			this.getCollection<T>(collection).findOne<T>(query).then(it => {
				if (it) {
					let instance = new ctor();
					Object.assign(instance, it);
					resolve(instance);
				} else {
					reject();
				}
			}).catch(reason => reject(reason));
		});
	}

	public static client: MongoClient;
	private static connected = false;
	private static config: Config;
}

export enum Collections {
	ARTICLES = 'articles',
	USERS = 'users',
	ROLES = 'roles',
}